package com.scau.jansing.thread.first.exchanger;

import java.util.*;
import java.util.concurrent.*;

/**
 * 交换两个线程的某个对象引用
 * Created by jansing on 2016/1/3.
 */
public class LearnExchanger {
    public static void main(String[] args) throws InterruptedException {
        ExchangerDemo.main(null);
    }
}

class ExchangerProducer<T> implements Runnable{
    private Generator<T> generator;
    private Exchanger<List<T>> exchanger;
    private List<T> holder;

    ExchangerProducer(Exchanger<List<T>> exchg, Generator<T> gen, List<T> holder){
        this.exchanger = exchg;
        this.generator = gen;
        this.holder = holder;
    }

    @Override
    public void run() {
        try{
            int n = 0;
            while(!Thread.interrupted()){
                n++;
                if(n<5){
                    System.out.println("Producer  ---->  " + holder.size());
                }
                for(int i=0; i<ExchangerDemo.size; i++){
                    holder.add(generator.next());
                }
                if(n<5){
                    System.out.println("Producer  ----> before exchange " + holder);
                    System.out.println("Producer  ---->  " + holder.size());
                }
                //阻塞，与Consumer交换holder的引用
                holder = exchanger.exchange(holder);
                if(n<5){
                    System.out.println("Producer  ----> after exchange " + holder);
                    System.out.println("Producer  ---->  " + holder.size());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ExchangerConsumer<T> implements Runnable{
    private Exchanger<List<T>> exchanger;
    private List<T> holder;
    private volatile T value;
    ExchangerConsumer(Exchanger<List<T>> ex, List<T> holder){
        this.exchanger = ex;
        this.holder = holder;
    }

    @Override
    public void run() {
        try{
            int i=0;
            while(!Thread.interrupted()){
                i++;
                if(i<5){
                    System.out.println("Consumer  ----->   before exchange" + holder);
                    System.out.println("Consumer  ----->  " + holder.size());
                }
                //阻塞，与Producer交换holder的引用
                holder = exchanger.exchange(holder);
                if(i<5){
                    System.out.println("Consumer  ----->   after exchange" + holder);
                    System.out.println("Consumer  ----->  " + holder.size());
                }
                for(T t : holder){
                    value = t;
                    holder.remove(t);
                }
                if(i<5){
                    System.out.println("Consumer  ----->  " + holder.size());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Final value : " + value);
    }
}

class ExchangerDemo{
    static int size = 10;
    static int delay = 5;

    public static void main(String[] args) throws InterruptedException {
        size = 10;
        delay = 1;
        ExecutorService exec = Executors.newCachedThreadPool();
        Exchanger<List<ExchangerDemo>> xc = new Exchanger<>();
        //CopyOnWriteArrayList 是多线程版的ArrayList，它的写操作是新建一个数组来实现写一致。
        //可能主要是防止这种情况：同时有读写线程，写线程删除了读线程的引用对象。
        List<ExchangerDemo> producerList = new CopyOnWriteArrayList<>();
        List<ExchangerDemo> consumerList = new CopyOnWriteArrayList<>();
        exec.execute(new ExchangerProducer<>(xc, BasicGenerator.create(ExchangerDemo.class), producerList));
        exec.execute(new ExchangerConsumer<>(xc, consumerList));
        TimeUnit.SECONDS.sleep(delay);
        exec.shutdownNow();

    }
}

interface Generator<T> { T next(); }

class BasicGenerator<T> implements Generator<T> {
    private Class<T> type;
    public BasicGenerator(Class<T> type){ this.type = type; }
    public T next() {
        try {
            // Assumes type is a public class:
            return type.newInstance();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
    // Produce a Default generator given a type token:
    public static <T> Generator<T> create(Class<T> type) {
        return new BasicGenerator<T>(type);
    }
}